# coding: utf-8
import os
import ast
import base64
import hashlib
import subprocess
from pathlib2 import Path
from .yaml_handle import ReadHandle
from .platforms import PlatformFile


class GetKeys:
    """获取全部字典的key, 包含列表中包含的字典"""

    @staticmethod
    def get_all_key(data):
        """
        :param data: {}, [{}]
        :return: key组成的列表, 未去重
        """
        all_key = []

        # noinspection PyShadowingNames
        def in_key(data):
            if isinstance(data, list):
                for item in data:
                    in_key(item)
            if isinstance(data, dict):
                for key, value in data.items():
                    if isinstance(value, (list, dict)):
                        in_key(value)
                    all_key.append(key)

            return all_key

        return in_key(data)


class SqlData:
    """获取yaml文件下的sql语句、用例数据等"""

    def __init__(self):
        self.plat = PlatformFile()

    def get_sql_sentence(self, pre_name: str, filename: str):
        abs_sql_path = self.plat.filePathPlat(pre_name, filename)
        return ReadHandle(abs_sql_path).yaml_files_read()


class SqlResult:
    """对sql查询的结果进行判断, 可以指定index获取返回结果"""

    @staticmethod
    def get_res(sql_query_res, index=None):
        if len(sql_query_res) != 0:
            return sql_query_res[0] if index in (0, None) or type(index) != int else sql_query_res[index]
        else:
            raise Exception("res is empty")


# noinspection PyUnusedLocal
def singleton(cls, *args, **kwargs):
    """单例"""
    instance = {}

    def _instance():
        if cls not in instance:
            instance[cls] = cls(*args, *kwargs)
        return instance[cls]

    return _instance


def dict_generator(indict, pre=None):
    """
    字典生成器
    :param indict:
    :param pre:
    :return:
    """
    try:
        pre = pre[:] if pre else []
        if isinstance(indict, dict):
            for key, value in indict.items():
                if isinstance(value, dict):
                    if len(value) == 0 and pre == []:
                        yield pre + [key, {}]
                    elif len(value) == 0 and pre != []:
                        yield ["_".join(pre) + '_' + key, {}]
                    else:
                        for d in dict_generator(value, pre + [key]): yield d
                elif isinstance(value, list):
                    if len(value) == 0 and pre == []:
                        yield pre + [key, []]
                    elif len(value) == 0 and pre != []:
                        yield ["_".join(pre) + '_' + key, []]
                    else:
                        yield ["_".join(pre) + '_' + key + '_list', value]
                        for v in value:
                            for d in dict_generator(v, pre + [key]): yield d
                elif isinstance(value, tuple):
                    if len(value) == 0 and pre == []:
                        yield pre + [key, ()]
                    elif len(value) == 0 and pre != []:
                        yield ["_".join(pre) + '_' + key, ()]
                    else:
                        for v in value:
                            for d in dict_generator(v, pre + [key]): yield d
                else:
                    if pre:
                        yield ["_".join(pre) + '_' + key, value]
                    else:
                        yield [key, value]
        elif isinstance(indict, list):
            for values in indict:
                for d in dict_generator(values):
                    yield d
        else:
            if pre: yield ["_".join(pre), indict]
    except BaseException as e:
        print(str(e))


# noinspection PyBroadException,SpellCheckingInspection
def response_data(indict):
    """
    通过数据生成字典
    :param indict:
    """
    try:
        temp_list, result, key = [],{}, []
        try:
            for i in dict_generator(indict):temp_list.append(i)
        except Exception as e:
            pass
        for value in temp_list:
            key.append(value[0])
        for keys in list(set(key)):
            result[keys]=[]
            for values in temp_list:
                if keys == values[0]:result[keys].append(values[1])
        result["source_response"] = indict
        return result
    except BaseException as e:
        print(str(e))


def isChinese(character):
    """传入字符串, 判断是否为纯中文"""
    for cha in character:
        if not '\u0e00' <= cha <= '\u9fa5':
            return False
    else:
        return True


def get_file_md5(file):
    """传入一个文件, 返回文件md5值"""
    m = hashlib.md5()
    with open(file, 'rb') as f:
        for line in f:
            m.update(line)
    return m.hexdigest()


def get_str_md5(string):
    """传入一个字符串, 返回字符串md5值"""
    if not isinstance(string, str):
        raise TypeError("只支持字符串类型")
    m = hashlib.md5()
    m.update(string.encode('utf-8'))
    return m.hexdigest()


# noinspection PyIncorrectDocstring
def get_str_sha1(string):
    """sha1 算法加密"""
    if not isinstance(string, str):
        raise TypeError("只支持字符串类型")
    sh = hashlib.sha1()
    sh.update(string.encode('utf-8'))
    return sh.hexdigest()


# noinspection PyIncorrectDocstring
def to_base64(string):
    """传入一个字符串, 返回字符串的Base64"""
    if not isinstance(string, str):
        raise TypeError("只支持字符串类型")
    base64_str = base64.b64encode(string.encode("utf-8"))
    return str(base64_str, 'utf-8')


# noinspection PyIncorrectDocstring
def from_base64(string):
    """传入一个Base64, 返回字符串"""
    if not isinstance(string, str):
        raise TypeError("只支持字符串类型")
    missing_padding = 4 - len(string) % 4
    if missing_padding:
        string += '=' * missing_padding
    return str(base64.b64decode(string), 'utf-8')


def ListFirstIdNodeName(lst: list):
    """传入一个数组, 返回数组的第一个元素的id和节点名称"""
    return lst[0].get("id"), lst[0].get("nodeName")


def shell(cmd):
    """cmd命令执行函数"""
    output, errors = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
    o = output.decode("utf-8")
    return o


def dictToUriStr(content: dict):
    """字典转为uri格式的字符串"""
    if not content:
        return ""
    res: str = "?"
    keys: list = list(content.keys())
    for index, item in enumerate(content):
        res += (item + "=")
        res += str(content.get(item))
        if index < len(keys) - 1:
            res += "&"
    return res


def strListToList(string):
    """字符串类型的列表转为列表"""
    # 如"['1', '2', '3']" ---> ['1', '2', '3']
    return ast.literal_eval(string)


def bytesToString(bytesContent):
    """字符串类型的列表转为列表"""
    # 如b"true" ---> true
    return str(bytesContent, encoding='utf-8')


def getFileInfo(filePath=None):
    """
    根据文件路径, 获取文件md5, size
    :param filePath: 文件路径
    """
    fileSize = os.path.getsize(filePath)
    fileName = get_str_md5(filePath.split("\\")[-1])
    return fileSize, fileName


def fileIsExist(file_path):
    """
    判断文件是否存在
    :param file_path:
    :return:
    """
    if not Path(file_path).exists():
        raise FileNotFoundError("请确认路径或文件是否正确！")
    return file_path


def getFileName(file_Path):
    """
    传入文件路径, 返回文件名称
    :param file_Path: 文件路径, 相对于项目根目录, 如 data/Doc/20191205.xls
    :return: 文件名
    """
    file = fileIsExist(file_Path)
    fileName = os.path.basename(file)
    return fileName


def bytesToStr(_bytes):
    """bytes转str"""
    return str(_bytes, encoding='utf-8')


def tenacity_item_node(value):
    """
    重试机制: 左侧树节点获取
    :param value: 被装饰器装饰的重试函数
    :return: 返回True会触发retry, 返回False不会触发retry
    """
    return True if len(value) == 0 else False


def retry_last_attempt(retry_state):
    """重试机制: 回调函数"""
    current_res = retry_state.outcome.result()
    # print("获取左侧树节点: {}".format(current_res))
    return current_res
